const BRA_L = '<';
const SPLASH = '/';
const BRA_R = '>';
const SPACE = " ";

const isQuote1 = (c) => (c === '"');
const isQuote2 = (c) => (c === "'");
const isSpace = (c) => (c === SPACE);
const isLBra = (c) => (c === BRA_L);
const isSlash = (c) => (c === SPLASH);
const isRBra = (c) => (c === BRA_R);
const isEqu = (c) => (c === '=');

const STATE_NAME = Symbol('Name');
const STATE_ATTRIBUTE = Symbol('Attribute');
const STATE_CONTENT = Symbol('Content');
const STATE_CLOSING = Symbol('Closing');
const STATE_CLOSED = Symbol('Closed');

const parse = (s) => {
    console.log('parse', s);
    let mem_tagname = [];
    let mem_attribute = [];
    let mem_content = [];
    let root = createNode('root');
    let nodeCur = root;
    let strCur = [];
    let inQuote1 = false;
    let inQuote2 = false;

    let lastChar = '';
    let state = STATE_CLOSED;

    const pNodeName = () => {
        console.log('pNodeName', mem_tagname.join(''));
        const node = createNode(mem_tagname.join(''));
        nodeCur.addChild(node);
        nodeCur = node;
        mem_tagname = [];
    }

    const pNodeContent = () => {
        if (cont) {
            cont = false;
            nodeCur && nodeCur.content(mem_content.join(''));
            mem_content = [];
        }
    }

    const pNodeAttribute = () => {
        console.log('pNodeAttribute', mem_attribute.join(''));
        const attr = mem_attribute.join('').split('=');
        const key = attr[0];
        const value = attr[1];
        if(key && key != "" && value){
            nodeCur.attr(key, value);
        }        
        mem_attribute = [];
    }

    const pNodeEnd = () => {

    }

    for (let i = 0; i < s.length; i++) {
        const char = s.charAt(i);
        let inQuote = inQuote1 || inQuote2;
        console.log(char);
        if (!inQuote && isLBra(char)) {
            strCur = mem_tagname;
            state = (state === STATE_CONTENT) ? STATE_CLOSING : STATE_NAME;
        } else if (!inQuote && isRBra(char)) {
            if (state === STATE_NAME) {
                pNodeName();
                state = STATE_CONTENT;
                strCur = mem_content;
            } else if (state === STATE_ATTRIBUTE) {
                pNodeAttribute();
                strCur = mem_content;
            } else if (state === STATE_CLOSING) {
                state = STATE_CLOSED;
                mem_tagname = [];
                nodeCur = nodeCur.parent;
            }
        } else if (!inQuote && isSpace(char)) {
            if (state === STATE_NAME) {
                pNodeName();
                strCur = mem_attribute;
                state = STATE_ATTRIBUTE;
            } else if (state === STATE_ATTRIBUTE) {
                pNodeAttribute();
                strCur = mem_attribute;
            }
        } else if (isSlash(char)) {
            if (isLBra(lastChar)) {
                state = STATE_CLOSING;
                strCur = mem_tagname;
            } else {
                strCur.push(char);
            }
        } else if (isQuote1(char)) {
            inQuote1 = !inQuote1;
        } else if (isQuote2(char)) {
            inQuote2 = !inQuote2;
        } else {
            strCur.push(char);
        }

        lastChar = char;
    }

    return root;
};

const createNode = (s) => (new Node(s));

class Node {
    constructor(name) {
        this.name = name;
        this.attributes = {};
        this.parent = null;
        this.children = [];
        this._content = '';
    }

    attr(key, value) {
        this.attributes[key] = value;
    }

    parent() {
        return this.parent;
    }

    addChild(child) {
        this.children.push(child);
        child.parent = this;
    }

    content(s) {
        this._content = s ? s : this._content;
        return this._content;
    }

    toString() {
        return `[${name}] `;
    }
}

Node.print = (n) => {
    console.log(`${n.name}`);
    if (n.children.length > 0) {
        for (let child of n.children) {
            Node.print(child);
        }
    }
}
